1 module hip.hipaudio.backend.openal.source; 2 version(OpenAL): 3 4 import hip.hipaudio.backend.openal.clip; 5 import hip.error.handler; 6 import hip.hipaudio.audio; 7 import hip.hipaudio.audiosource; 8 import hip.util.memory; 9 import hip.hipaudio.backend.openal.al_err; 10 import bindbc.openal; 11 12 13 /** 14 * Constant used for making the panning distance offset from the listener 15 */ 16 enum ALfloat PANNING_CONSTANT = 1000; 17 18 public class HipOpenALAudioSource : HipAudioSource 19 { 20 import hip.console.log; 21 //id is created from OpenAL player 22 uint id; 23 bool isStreamed; 24 25 this(bool isStreamed) 26 { 27 this.isStreamed=isStreamed; 28 alGenSources(1, &id); 29 alCheckError("Error creating OpenAL source"); 30 alSourcef(id, AL_GAIN, 1); 31 alSourcef(id, AL_PITCH, 1); 32 alSource3f(id, AL_POSITION, 0f, 0f, 0f); 33 alSource3f(id, AL_VELOCITY, 0f, 0f, 0f); 34 alSourcei(id, AL_LOOPING, AL_FALSE); 35 alCheckError("Error setting OpenAL source properties"); 36 } 37 38 alias pitch = AHipAudioSource.pitch; 39 alias panning = AHipAudioSource.panning; 40 alias volume = AHipAudioSource.volume; 41 alias pitch = AHipAudioSource.pitch; 42 alias clip = HipAudioSource.clip; 43 alias position = AHipAudioSource.position; 44 alias loop = AHipAudioSource.loop; 45 46 47 override float pitch(float value) 48 { 49 auto ret = super.pitch(value); 50 if(isDirty && isPlaying) 51 { 52 alSourcef(id, AL_PITCH, ret); 53 alCheckError("Error setting OpenAL source pitch"); 54 isDirty = false; 55 } 56 return ret; 57 } 58 59 override float panning(float value) 60 { 61 auto ret = super.panning(value); 62 if(isDirty && isPlaying) 63 { 64 isDirty = false; 65 alSource3f(id, AL_POSITION, position[0] + (ret*PANNING_CONSTANT), position[1], position[2]); 66 alCheckError("Error setting OpenAL source position/panning"); 67 } 68 return ret; 69 } 70 override float volume(float value) 71 { 72 auto ret = super.volume(value); 73 if(isDirty && isPlaying) 74 { 75 isDirty = false; 76 alSourcef(id, AL_GAIN, ret); 77 alCheckError("Error setting OpenAL source volume"); 78 } 79 return ret; 80 } 81 82 override float[3] position(float[3] value) 83 { 84 auto ret = super.position(value); 85 if(isDirty && isPlaying) 86 { 87 isDirty = false; 88 alSource3f(id, AL_POSITION, ret[0] + (panning*PANNING_CONSTANT), ret[1], ret[2]); 89 alCheckError("Error setting OpenAL source position/panning"); 90 } 91 return ret; 92 } 93 94 override bool loop(bool value) 95 { 96 bool ret = super.loop(value); 97 if(isDirty && isPlaying) 98 { 99 alSourcei(id, AL_LOOPING, ret ? AL_TRUE : AL_FALSE); 100 alCheckError("Error setting OpenAL loop"); 101 } 102 return ret; 103 } 104 105 public void setDistanceModel(DistanceModel model) 106 { 107 alDistanceModel(getALDistanceModel(model)); 108 alCheckError("Error setting OpenAL source distance model"); 109 } 110 /** 111 * After the max distance, the volume won't decrease anymore 112 */ 113 public void setMaxDistance(float dist) 114 { 115 alSourcef(id, AL_MAX_DISTANCE, dist); 116 alCheckError("Error setting OpenAL source max distance"); 117 } 118 119 /** 120 * Sets the distance where the volume will be equal to 1 121 */ 122 void setReferenceDistance(float dist) 123 { 124 alSourcef(id, AL_REFERENCE_DISTANCE, dist); 125 alCheckError("Error setting OpenAL source reference distance"); 126 } 127 /** 128 * The factor which the sound volume decreases when the distance is greater 129 * than the reference 130 */ 131 public void setRolloffFactor(float factor) 132 { 133 alSourcef(id, AL_ROLLOFF_FACTOR, factor); 134 alCheckError("Error setting OpenAL source rolloff factor"); 135 } 136 137 public void setVelocity(in float[3] vel) 138 { 139 alSource3f(id, AL_VELOCITY, vel[0], vel[1], vel[2]); 140 alCheckError("Error setting OpenAL source velocity"); 141 142 } 143 void setDoppler(in float[3] vel) 144 { 145 alSource3f(id, AL_DOPPLER_VELOCITY, vel[0], vel[1], vel[2]); 146 alCheckError("Error setting OpenAL source doppler factor"); 147 } 148 149 override bool play() 150 { 151 HipOpenALClip clp = clip.getAudioClipBackend!(HipOpenALClip); 152 if(clp.hasBuffer) 153 { 154 if(isDirty) 155 { 156 isDirty = false; 157 alSourcef(id, AL_PITCH, pitch); 158 alCheckError("Error setting OpenAL source pitch"); 159 alSourcef(id, AL_GAIN, volume); 160 alCheckError("Error setting OpenAL source volume"); 161 alSource3f(id, AL_POSITION, position[0] + (panning*PANNING_CONSTANT), position[1], position[2]); 162 alCheckError("Error setting OpenAL source position/panning"); 163 alSourcei(id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); 164 alCheckError("Error setting OpenAL loop"); 165 } 166 alSourcePlay(id); 167 alCheckError("Error querying OpenAL play"); 168 return true; 169 } 170 else 171 ErrorHandler.showErrorMessage("Tried to play without a buffer", ""); 172 return false; 173 } 174 175 public bool resume() 176 { 177 if(!isPlaying) 178 { 179 alSourcePlay(id); 180 alCheckError("Error querying OpenAL play"); 181 return true; 182 } 183 return false; 184 } 185 186 187 override bool stop() 188 { 189 alSourceStop(id); 190 alCheckError("Error querying OpenAL stop"); 191 return false; 192 } 193 194 override bool pause() 195 { 196 alSourcePause(id); 197 alCheckError("Error querying OpenAL pause"); 198 return false; 199 } 200 override bool play_streamed(){return play();} 201 202 203 override IHipAudioClip clip(IHipAudioClip newClip) 204 { 205 super.clip(newClip); 206 // if(!newClip.isStreamed) 207 // { 208 // HipAudioBuffer buf = getBufferFromAPI(newClip); 209 // alSourcei(id, AL_BUFFER, buf.al); 210 // } 211 // else 212 { 213 HipAudioBuffer buf = getBufferFromAPI(newClip); //use clip.chunkSize in future 214 alSourceQueueBuffers(id, 1, &buf.al); 215 } 216 logln(id); 217 return newClip; 218 } 219 220 221 override void pullStreamData() 222 { 223 ErrorHandler.assertExit(clip !is null, "Can't pull stream data without any buffer attached"); 224 ErrorHandler.assertExit(id != 0, "Can't pull stream data without source id"); 225 226 HipAudioBuffer buffer; 227 int processed; 228 alGetSourcei(id, AL_BUFFERS_PROCESSED, &processed);//Gets the queueId 229 buffer.al = cast(uint)processed; 230 if(buffer.al != 0) 231 { 232 //Returns the bufferId to freeBuf 233 alSourceUnqueueBuffers(id, 1, &buffer.al); 234 sendAvailableBuffer(buffer); 235 } 236 clip.updateStream(); 237 238 HipOpenALClip c = cast(HipOpenALClip)clip; 239 buffer = c.getBuffer(c.getClipData(), c.chunkSize); 240 alSourceQueueBuffers(id, 1, &buffer.al); 241 242 } 243 244 245 uint getALFreeBuffer() 246 { 247 int b; 248 alGetSourcei(id, AL_BUFFERS_PROCESSED, &b); 249 return cast(uint)b; 250 } 251 252 override HipAudioBufferWrapper* getFreeBuffer() 253 { 254 HipAudioBuffer buffer; 255 int b; 256 alGetSourcei(id, AL_BUFFERS_PROCESSED, &b); 257 buffer.al = cast(uint)b; 258 if(b == 0) 259 return null; 260 return (cast(HipAudioClip)clip).findBuffer(buffer); 261 } 262 263 264 265 ~this() 266 { 267 logln("HipAudioSource Killed!"); 268 alDeleteSources(1, &id); 269 id = 0; 270 } 271 } 272 273 ALenum getALDistanceModel(DistanceModel model) 274 { 275 final switch(model) with(DistanceModel) 276 { 277 case DISTANCE_MODEL: return AL_DISTANCE_MODEL; 278 case INVERSE: return AL_INVERSE_DISTANCE; 279 case INVERSE_CLAMPED: return AL_INVERSE_DISTANCE_CLAMPED; 280 case LINEAR: return AL_LINEAR_DISTANCE; 281 case LINEAR_CLAMPED: return AL_LINEAR_DISTANCE_CLAMPED; 282 case EXPONENT: return AL_EXPONENT_DISTANCE; 283 case EXPONENT_CLAMPED: return AL_EXPONENT_DISTANCE_CLAMPED; 284 } 285 }